Skip to content

Decode progressive JPEG images - #29

Merged
MPCoreDeveloper merged 4 commits into
mainfrom
feat/progressive-jpeg
Nov 15, 2026
Merged

MPCoreDeveloper merged 4 commits into
mainfrom
feat/progressive-jpeg

Conversation

@MPCoreDeveloper

@MPCoreDeveloper MPCoreDeveloper commented Nov 15, 2026 •

Copy link
Copy Markdown
Owner

What

DCTDecode now reads progressive JPEG (SOF2) as well as baseline/extended (SOF0/SOF1): spectral selection and successive approximation accumulate in one quantised coefficient grid per component, and the inverse DCT runs once at the end. The DC first scan, the DC refinement, the AC first scan with EOB runs and ZRL, and the AC refinement with correction bits and new coefficients are all read; a non-interleaved AC scan (one component, its own block grid) and an interleaved DC scan land on the same blocks.

Fixtures

Seven Pillow-written references under tests/SharpCorePDF.Tests/Assets/jpeg/, each compared with Pillow's decode within two levels of every sample, plus a nine-point truncated-stream theory:

  • jpeg-progressive.jpg — 32 x 32, quality 95, 4:4:4 (DC first, DC refine, AC first, AC refine)
  • jpeg-progressive-420.jpg — 4:2:0 (interleaved DC with six blocks per MCU, non-interleaved chroma AC)
  • jpeg-progressive-gray.jpg — one component
  • jpeg-progressive-cmyk.jpg — four components, plain CMYK (inverted output, as the baseline CMYK test does)
  • jpeg-progressive-wide.jpg — 32 x 16, so the column and row block counts differ
  • jpeg-progressive-sparse.jpg — flat areas, so the EOB runs and the zero runs (ZRL) are used
  • jpeg-progressive-restart.jpg — a DRI interval and 150 restart markers, so the predictors and the EOB run reset

The last four were added after SonarCloud's new-code gate asked for them (see Mutation).

The two conventions that cost a round

An AC refinement scan reads the sign bit of a new coefficient before the correction bits of the coefficients the run passes over, and 1 means positive (0 negative) — the inverse of the magnitude field an AC first scan uses. Either backwards still decodes every scan and leaves a handful of coefficients one or two steps out, which is why the acceptance is the pixel comparison. Written down in ADR-005.

Mutation

Scoped reading of Pdf/JpegDecoder.cs: 76.81% (710 killed + 2 timeouts, 127 survived, 88 no coverage, 1122 mutants; the new region — line 520 onwards — is 82.5%). The first reading, before the four coverage fixtures, was 70.23% with 72.0% in the new region; those fixtures killed the survivors it named (restart interval, sparse/ZRL, CMYK, non-square grid) and moved SonarCloud's new-code coverage from 78.35% (42 uncovered of 194) to 92.27% (15 uncovered of 194). What remains is the boundary and error end of the decoder — a DC difference that needs eleven bits, a coefficient exactly on Se, a DC refinement at Al > 0, the YCCK transform, a 4:2:2 or odd-sized image, a missing table, a budget boundary, and a few bit-reader branches. Recorded in docs/mutation-testing.md, not papered over.

The reading was taken with a local 5.0.0 because 5.1.0 on this workstation ignores every form of scope: the mutate entry in stryker-config.json, --mutate and --since:main all leave 23,042 mutants, and project mode is refused ("Test projects cannot be specified when running Stryker in solution context"). 5.0.0 project mode with the command-line --mutate applies the filter (839 of 20,004). The pin and the workflow are unchanged; the note in the record has the evidence.

Validation

  • dotnet build SharpCorePDF.slnx -c Release: 0 warnings, 0 errors
  • PdfJpegFilterTests: 24/24 (11 earlier + 4 progressive variants + 9 truncated-stream cases)
  • Full suite: 1266/1266 on net10.0 and net11.0
  • SonarCloud new-code coverage: 92.27% (gate 80%, repository target 90%)
  • Docs: ADR-005 status, criteria and status table, parity row 14, CHANGELOG, manual chapter 8, plus the mutation record

@MPCoreDeveloper
MPCoreDeveloper merged commit d179d09 into main Nov 15, 2026
8 checks passed
@MPCoreDeveloper
MPCoreDeveloper deleted the feat/progressive-jpeg branch November 15, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant